﻿namespace UDKDevKitVSXDebugEngine
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using Microsoft.VisualStudio.Debugger.Interop;
    using UDKDevKitVSXDebugEngine.Definitions;
    using UDKDevKitVSXDebugEngine.Extentions;

    public class DebuggerExpression : Expression, IDebugExpression2
    {
        private VariableInformation var;

        public DebuggerExpression(string code, Engine engine) : base(code, engine)
        {
        }

        public DebuggerExpression(VariableInformation var, Engine engine)
            : this(String.Empty, engine)
        {
            this.var = var;
        }

        // This method cancels asynchronous expression evaluation as started by a call to the IDebugExpression2::EvaluateAsync method.
        int IDebugExpression2.Abort()
        {
            return Constants.S_OK;
        }

        // This method evaluates the expression asynchronously.
        // This method should return immediately after it has started the expression evaluation. 
        // When the expression is successfully evaluated, an IDebugExpressionEvaluationCompleteEvent2 
        // must be sent to the IDebugEventCallback2 event callback
        //
        // This is primarily used for the immediate window which this engine does not currently support.
        int IDebugExpression2.EvaluateAsync(enum_EVALFLAGS dwFlags, IDebugEventCallback2 pExprCallback)
        {
            Engine.ExecSimulator.RaiseDebugEventsExpressionEvaluationComplete(this);

            if (this.var != null)
            {
                Debug.WriteLine("IDebugExpression2::EvaluateAsync " + var.Name + " = " + var.Value);
            }
            
            return Constants.S_OK;
        }

        // This method evaluates the expression synchronously.
        int IDebugExpression2.EvaluateSync(enum_EVALFLAGS dwFlags, uint dwTimeout, IDebugEventCallback2 pExprCallback, out IDebugProperty2 ppResult)
        {
            ppResult = null;

            if (this.Value != null)
            {
                ppResult = new Property(this.Value);
                return Constants.S_OK;
            }

            return Constants.E_INVALIDARG;
        }

        public override object Evaluate()
        {
            return this.Value;
        }

        public override bool Equals(object obj)
        {
            var expression = obj as Expression;
            if (expression != null)
            {
                return this.ToString().Equals(expression.ToString());
            }

            return base.Equals(obj);
        }

        public override string ToString()
        {
            if (this.Value == null)
            {
                return "Null";
            }

            return this.Value.ToString();
        }

        protected VariableInformation Value
        {
            get
            {
                if (this.var == null)
                {
                    this.CalculateExpression();
                }

                return this.var;
            }
        }

        protected bool CalculateExpression()
        {
            if (!ProcessVariables(ExpressionText, Engine.DebugContext.Locals, out this.var))
            {
                return ProcessVariables(ExpressionText, Engine.DebugContext.Globals, out this.var);
            }

            return true;
        }

        private static bool ProcessVariables(string variableExpression, IList<VariableInformation> variables, out VariableInformation value)
        {
            string variablePath = variableExpression;

            string leftPart;
            string rightPart;
            var pos = variablePath.IndexOf('.');
            if (pos != -1)
            {
                leftPart = variablePath.Substring(0, pos);
                rightPart = variablePath.Substring(pos + 1, variablePath.Length - pos - 1);
            }
            else
            {
                leftPart = variablePath;
                rightPart = String.Empty;
            }

            foreach (VariableInformation currVariable in variables)
            {
                if (string.Compare(currVariable.Name, leftPart, true) == 0)
                {
                    if (String.IsNullOrEmpty(rightPart))
                    {
                        value = currVariable;
                        return true;
                    }
                    else if (currVariable.HasChildren)
                    {
                        if (ProcessVariables(rightPart, currVariable.Children, out value))
                        {
                            return true;
                        }
                    }
                }
                else if (string.Compare(currVariable.Value, "[[ Base Class ]]", true) == 0)
                {
                    if (ProcessVariables(variableExpression, currVariable.Children, out value))
                    {
                        return true;
                    }
                }
            }

            value = null;
            return false;
        }
    }
}
